package com.recharge.service.httpInterFace;

import com.alibaba.fastjson.JSONObject;
import com.recharge.common.result.ResultDO;
import com.recharge.common.result.ResultSupport;
import com.recharge.common.util.Constant;
import com.recharge.common.util.MD5Util;
import com.recharge.common.util.TemplateUtils;
import com.recharge.common.util.http.Encode;
import com.recharge.common.util.http.HttpClientUtil;
import com.recharge.common.util.http.HttpUtil;
import com.recharge.common.util.http.MapTool;
import com.recharge.domain.query.OrderStatusQueryDO;
import com.recharge.domain.vo.GoodsChannelDO;
import com.recharge.domain.vo.ProviderApiDO;
import com.recharge.domain.vo.ProviderCallbackDO;
import com.recharge.service.ProviderApiService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@Service
public class XiaoSiHTTPImpl implements ProviderHTTPService {

    private Logger logger = LoggerFactory.getLogger(this.getClass().getName());

    @Autowired
    ProviderApiService providerApiService;

    @Override
    public ResultDO commitOrder(GoodsChannelDO domain) {
        if (TemplateUtils.isEmpty(domain.getProviderSerialNumber())) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("订单提交 >> 订单号为空 %s", domain.toString())
                    , "订单号为空"
                    , null);
        }
        if(domain.getServiceProvider() != 3){
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("订单提交 >> 订单%s 仅能提交联通订单", domain.getProviderSerialNumber())
                    , "仅能提交联通订单"
                    , null);
        }
        //供应商ID
        String mchId = domain.getUserId();
        //充值号码
        String tel = domain.getRechargeNumber();
        //请求金额(元)
        Long price = domain.getAmount() / 1000;
        //运营商
        String teltype = "1";
        //到账范围值
        String timeout = "50";
        //回调地址
        String notify = domain.getCallback();
        // 设置日期格式  加密签名
        String time = System.currentTimeMillis() + "";
        //产生(100000,999999]
        String rand = String.valueOf((int)(Math.random()*(999999-100000+1)+100000));
        //供应商密钥
        String privateKey = domain.getPrivateKey();
        //平台流水号
        String serialNumber = domain.getProviderSerialNumber();
        //签名
        String afterSign = "";
        try {
            String beforeSign =mchId+tel+price+teltype+timeout+notify+time+rand+privateKey;

            logger.debug("before_sign: " + beforeSign);
            afterSign = MD5Util.md5(beforeSign);
        } catch (Exception e) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("订单提交 >> 渠道商ID: %s HTTP请求参数构建失败", domain.getProviderId())
                    , "HTTP请求参数构建失败"
                    , null);
        }

        Map<String, String> requestParamMap = new HashMap<>();
        requestParamMap.put("mchid", mchId);
        requestParamMap.put("tel", tel);
        requestParamMap.put("price", String.valueOf(price));
        requestParamMap.put("teltype", teltype);
        requestParamMap.put("timeout", timeout);
        requestParamMap.put("notify", notify);
        requestParamMap.put("time", time);
        requestParamMap.put("rand", rand);
        requestParamMap.put("sign", afterSign);
        // 发送请求
        logger.warn(String.format("订单提交 >> 发送POST请求(订单号%s): %s?%s", domain.getProviderSerialNumber(), domain.getRequestUrl(), requestParamMap.toString()));
        //发送HTTP POST请求
//        String sendResult = HttpClientUtil.doPost(domain.getRequestUrl(), requestParamMap);

        String sendResult = null;

        Map<String, String> httpCodeMap = new HashMap<>();
        try {
            sendResult = HttpUtil.post(domain.getRequestUrl(), MapTool.join(requestParamMap, v -> v == null ? null : Encode.urlEncode(v.toString())), conn -> {
                    try {
                        int code = conn.getResponseCode();
                        httpCodeMap.put("httpCode",String.valueOf(code));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            );
        } catch (Exception e) {
            e.printStackTrace();
        }

        if(TemplateUtils.isEmpty(sendResult) && httpCodeMap.get("httpCode") != null && !"200".equals(httpCodeMap.get("httpCode"))){
            return returnFail(false
                , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                , String.format("订单提交 >> 订单号 %s -> 未正常提交成功，需人工核实", domain.getOrderNo())
                , "HTTP请求失败"
                , sendResult);
        }
        logger.warn(String.format("订单提交 >> 请求返回信息: 供货流水%s -> %s", domain.getProviderSerialNumber(), sendResult));
        // 解析请求返回结果转换为json对象
        JSONObject resultJson = new JSONObject();
        try {
            resultJson = JSONObject.parseObject(sendResult);
        } catch (Exception e) {
            logger.debug(String.format("订单提交 >> 请求返回结果解析json失败: %s", sendResult));
        }
        if (resultJson == null || resultJson.size() == 0) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 供货流水 %s -> 订单提交没有返回信息", domain.getProviderSerialNumber())
                    , "没有返回信息"
                    , sendResult);
        }
        String code = null;
        String desc = "";
        String providerOrderId = "";
        try {
            code = resultJson.get("code").toString();
            desc = resultJson.get("msg").toString();
            providerOrderId = resultJson.get("order_id").toString();
        } catch (Exception e) {
            logger.debug(String.format("获取返回信息失败: 供货流水%s -> %s", domain.getProviderSerialNumber(), sendResult));
        }
        if (code == null) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 供货流水 %s -> 没有获取到返回状态码", domain.getProviderSerialNumber())
                    , "订单提交没有返回状态码"
                    , sendResult);
        }
        // 状态码: 下单失败
        String[] array = {"100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111"};
        //状态码判断
        if ("0".equals(code)) { //成功
            //写入存根
            ProviderCallbackDO callbackDO = new ProviderCallbackDO();
            callbackDO.setSerialNumber(providerOrderId);
            callbackDO.setProviderId(domain.getProviderId());
            callbackDO.setStatus(Constant.CALL_BACK_PROCESS);
            callbackDO.setOrderNo(domain.getOrderNo());
            ResultDO result = providerApiService.addCallback(callbackDO);
            if (!result.isSuccess()) {
                return returnFail(false
                        , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                        , String.format("订单提交 >> 订单号%s 添加存根信息失败", domain.getOrderNo())
                        , "添加回调存根信息失败"
                        , null);
            }
            logger.debug(String.format("订单提交 >> 供货流水 %s -> 订单提交成功|%s %s 上游同步返回的订单号：%s", domain.getProviderSerialNumber(), code, desc, providerOrderId));
            result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.BUSINESS_RECHARGE_RETURN_STATUS_SUCCESS);
            result.setModel(ResultDO.SECOND_MODEL_KEY, providerOrderId);
            return result;
        } else if(Arrays.asList(array).contains(code)){
            return returnFail(true
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("订单提交 >> 供货流水 %s -> 订单提交失败|%s %s", domain.getProviderSerialNumber(), code, desc)
                    , String.format("%s %s", desc, code)
                    , sendResult);
        } else {
            return returnFail(true
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 供货流水 %s -> 订单提交未正确定义状态码|%s %s", domain.getProviderSerialNumber(), code, desc)
                    , String.format("%s %s", desc, code)
                    , sendResult);
        }
    }

    @Override
    public ResultDO backOrder(ProviderApiDO domain, String paramStr, String bodyStr) {
        ResultDO result = new ResultSupport();
        JSONObject jsonObject = null;
        try {
            jsonObject = JSONObject.parseObject(paramStr);
            logger.debug("订单异步回调 >> " + domain.getProviderNo() + "解析后数据: " + jsonObject.toJSONString());
        } catch (Exception e) {
            logger.debug("订单异步回调 >> " + domain.getProviderNo() + "解析json失败:" + paramStr);
        }
        if (jsonObject == null) {
            logger.debug("订单异步回调 >> 收到异步回调,但是没有回调信息");
            result.setSuccess(false);
            result.setModel(ResultDO.FIRST_MODEL_KEY, "fail");
            return result;
        }

        String mchid, order_id, tel, price, status, pay_type, returnSign, voutcher = "";
        try {
            //商户ID
            mchid = jsonObject.getString("mchid");
            //上游流水号
            order_id = jsonObject.getString("order_id");
            //充值号码
            tel = jsonObject.getString("tel");
            //充值金额
            price = jsonObject.getString("price");
            //交易类型
            pay_type = jsonObject.getString("pay_type");
            //状态
            status = jsonObject.getString("status");
            // 回调签名
            returnSign = jsonObject.getString("sign");
            //充值凭证
            if (!TemplateUtils.isEmpty(jsonObject.get("out_order_id"))) {
                voutcher = jsonObject.getString("out_order_id");
            }
        } catch (Exception e) {
            logger.debug("订单异步回调 >> 获取返回结果异常" + e.getMessage());
            result.setSuccess(false);
            result.setModel(ResultDO.FIRST_MODEL_KEY, "fail");
            return result;
        }
        //加密前签名
        String beforeVerifySign = mchid + order_id + voutcher + tel + price+pay_type + status + domain.getPrivateKey();

        String afterVerifySign = "";
        try {
            afterVerifySign = MD5Util.md5(beforeVerifySign, 32);
        } catch (Exception e) {
            logger.debug("订单异步回调 >> 上游流水号(" + order_id + ")验签加密失败" + e.getMessage());
        }
//        logger.debug("签名验证: after >>"+afterVerifySign);
        if (!afterVerifySign.equals(returnSign)) {
            logger.debug("订单异步回调 >> 签名校验: 加密前 >>" + beforeVerifySign);
            logger.debug(String.format("订单异步回调 >> 上游流水号:%s 签名校验不一致 {本地签名:%s 返回签名:%s }", order_id, afterVerifySign, returnSign));
            result.setModel(ResultDO.FIRST_MODEL_KEY, "fail");
            return result;
        }

        //判断订单状态
        if (status.equals("1")) {
            logger.debug(String.format("订单异步回调 >> 订单异步返回:供货流水 %s 成功 status: %s", order_id, status));
            result = providerApiService.updateCallback(order_id, voutcher, order_id, Constant.CALL_BACK_SUCCESS);
        } else if (status.equals("0")) {
            logger.debug(String.format("订单异步回调 >> 订单异步返回:供货流水 %s 失败 status: %s", order_id, status));
            result = providerApiService.updateCallback(order_id, "", order_id, Constant.CALL_BACK_FAIL);
        } else if (status.equals("2")) {
            logger.debug(String.format("订单异步回调 >> 订单异步返回:供货流水 %s 支付中 status: %s", order_id, status));
            result.setModel(ResultDO.FIRST_MODEL_KEY, "fail");
            return result;
        }
        if (!result.isSuccess()) {
            logger.debug("订单异步回调 >> 更新订单(" + order_id + ")状态失败");
            result.setModel(ResultDO.FIRST_MODEL_KEY, "fail");
            return result;
        }
        result.setModel(ResultDO.FIRST_MODEL_KEY, "success");
        return result;
    }

    @Override
    public ResultDO queryOrder(OrderStatusQueryDO domain) {
        ResultDO result = new ResultSupport();
        //供应商ID
        String mchId = domain.getUserId();
        //供应商ID
        String order_id = domain.getSerialNo();
        // 设置日期格式  加密签名
        String time = System.currentTimeMillis() + "";
        //产生(100000,999999]
        String rand = String.valueOf((int)(Math.random()*(999999-100000+1)+100000));
        //供应商密钥
        String privateKey = domain.getPrivateKey();

        Map<String, String> requestParamMap = new HashMap<>();
        requestParamMap.put("mchid", mchId);
        requestParamMap.put("order_id", order_id);
        requestParamMap.put("time", time);
        requestParamMap.put("rand", rand);
        String sign = "";
        try {
            sign = MD5Util.md5(mchId + order_id + time + rand + privateKey);
        } catch (Exception e) {
            logger.debug("查询订单 >> 签名加密失败" + e.getMessage());
            result.setSuccess(false);
            result.setModel(ResultDO.THIRD_MODEL_KEY, "签名加密失败");
            return result;
        }
        requestParamMap.put("sign", sign);

        // 发送请求
        logger.debug(String.format("查询订单 >> 发送POST请求(供货流水%s): url %s , param %s", domain.getSerialNo(), domain.getOrderFindUrl(), requestParamMap));
        String sendResult = HttpClientUtil.doPost(domain.getOrderFindUrl(), requestParamMap);
        switch (sendResult) {
            case "500":
                logger.debug(String.format("查询订单 >> 订单 %s -> HTTP请求失败|500", domain.getSerialNo()));
                result.setSuccess(false);
                result.setModel(ResultDO.THIRD_MODEL_KEY, "HTTP请求失败");
                return result;
            case "504":
                logger.debug(String.format("查询订单 >> 订单 %s -> HTTP请求超时|504", domain.getSerialNo()));
                result.setSuccess(false);
                result.setModel(ResultDO.THIRD_MODEL_KEY, "HTTP请求超时");
                return result;
            case "":
                logger.debug(String.format("查询订单 >> 订单 %s -> 请求没有返回信息", domain.getSerialNo()));
                result.setSuccess(false);
                result.setModel(ResultDO.THIRD_MODEL_KEY, "请求没有返回信息");
                return result;
        }
        logger.debug(String.format("查询订单 >> 请求返回信息: 订单 %s -> %s", domain.getSerialNo(), sendResult));

        // 解析请求返回结果转换为json对象
        JSONObject resultJson = new JSONObject();
        try {
            resultJson = JSONObject.parseObject(sendResult);
        } catch (Exception e) {
            logger.debug(String.format("查询订单 >> 请求返回结果解析XML失败: %s", sendResult));
        }
        if (resultJson.size() == 0) {
            logger.debug("查询订单 >> 未找到请求返回结果");
            result.setSuccess(false);
            result.setModel(ResultDO.THIRD_MODEL_KEY, "未找到请求返回结果");
            return result;
        }

        String code = null;
        String desc = "";
        try {
            code = resultJson.get("code").toString();
            desc = resultJson.get("msg").toString();
        } catch (Exception e) {
            logger.debug(String.format("查询订单 >> 获取返回信息失败: 订单 %s -> %s", domain.getSerialNo(), sendResult));
        }
        if (code == null) {
            logger.debug("查询订单 >> 未获取到返回状态码");
            result.setSuccess(false);
            result.setModel(ResultDO.THIRD_MODEL_KEY, "未获取到返回状态码");
            return result;
        }

        // 查询状态码: 查询失败
        String[] array = {"100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111"};

        if ("0".equals(code)) { //请求成功
            String status = "";
            String voucher = "";
            try {
                JSONObject data = resultJson.getJSONObject("data");
                if (data == null || data.size() == 0) {
                    logger.debug("查询订单 >> 未获取到返回信息结果集");
                    result.setSuccess(false);
                    result.setModel(ResultDO.THIRD_MODEL_KEY, "未获取到返回信息结果集");
                    return result;
                }
                status = data.getString("status");
//                desc = data.getString("statusDesc");
                if (data.get("out_order_id") != null) {
                    voucher = data.getString("out_order_id");
                }
            } catch (Exception e) {
                logger.debug("查询订单 >> 未获取到返回结果集信息");
                result.setSuccess(false);
                result.setModel(ResultDO.THIRD_MODEL_KEY, "未获取到返回信息结果集");
                return result;
            }

            if (status.equals("1")) { //成功
                logger.debug(String.format("查询订单 >> 订单 %s -> 订单成功|%s", domain.getSerialNo(), desc));
                result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_SUCCESS);
                result.setModel(ResultDO.SECOND_MODEL_KEY, voucher);
            } else if (status.equals("0")) {//失败
                logger.debug(String.format("查询订单 >> 订单 %s -> 订单失败|%s", domain.getSerialNo(), desc));
                result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_FAIL);
            } else if (status.equals("3")) {//充值中
                logger.debug(String.format("查询订单 >> 订单 %s -> 订单充值中|%s", domain.getSerialNo(), desc));
                result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_PROCESS);
            } else {//未知
                logger.debug(String.format("查询订单 >> 订单 %s -> 订单未知|%s %s", domain.getSerialNo(), desc, status));
                result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_NUKNOW);
            }
        } else if (Arrays.asList(array).contains(code)) {//请求失败
            logger.debug(String.format("查询订单 >> 订单 %s -> 订单查询请求失败|%s", domain.getSerialNo(), desc));
            result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_FAIL);
        } else {//未知
            logger.debug(String.format("查询订单 >> 订单 %s -> 订单查询请求失败|%s", domain.getSerialNo(), desc));
            result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_NUKNOW);
        }
        result.setModel(ResultDO.THIRD_MODEL_KEY, desc);
        return result;
    }

    @Override
    public ResultDO queryBalance(ProviderApiDO domain) {
        ResultDO result = new ResultSupport();
        if (TemplateUtils.isEmpty(domain.getUserId()) || TemplateUtils.isEmpty(domain.getPrivateKey())) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("余额查询失败 >> 参数丢失", domain.toString())
                    , "参数丢失"
                    , null);
        }
        //供应商ID
        String mchId = domain.getUserId();
        // 设置日期格式  加密签名
        String time = System.currentTimeMillis() + "";
        //产生(100000,999999]
        String rand = String.valueOf((int)(Math.random()*(999999-100000+1)+100000));
        //供应商密钥
        String privateKey = domain.getPrivateKey();
        //签名
        String afterSign = "";
        try {
            String beforeSign =mchId+time+rand+privateKey;

            logger.debug("before_sign: " + beforeSign);
            afterSign = MD5Util.md5(beforeSign);
        } catch (Exception e) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("余额查询 >> 供应商账号: "+domain.getUserId()+" 签名加密失败" + e.getMessage())
                    , "签名失败"
                    , null);
        }
        Map<String, String> requestParamMap = new HashMap<>();
        requestParamMap.put("mchid", mchId);
        requestParamMap.put("time", time);
        requestParamMap.put("rand", rand);
        requestParamMap.put("sign", afterSign);

        logger.debug(String.format("余额查询 >> 余额查询信息: 供应商账号 %s -> %s", domain.getUserId(), requestParamMap));
        // 发送请求
        String sendResult = HttpClientUtil.doPost(domain.getBalanceFindUrl(), requestParamMap);

//        String params = MapTool.join(requestParamMap);
//        String sendResult = null;
//        Map<String, String> httpCodeMap = new HashMap<>();
//        System.out.println(domain.getBalanceFindUrl());
//        System.out.println(params);
//        try {
//            sendResult = HttpUtil.post(domain.getBalanceFindUrl(), params, conn -> {
//                conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
//                try {
//                    int code = conn.getResponseCode();
//                    httpCodeMap.put("httpCode",String.valueOf(code));
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            });
//        } catch (Exception e) {
//            e.printStackTrace();
//        }

        logger.debug(String.format("余额查询 >> 余额查询返回信息: 供应商账号 %s -> %s", domain.getUserId(), sendResult));

        // 解析请求返回结果转换为json对象
        JSONObject resultJson = new JSONObject();
        try {
            resultJson = JSONObject.parseObject(sendResult);
        } catch (Exception e) {
            logger.debug(String.format("余额查询 >> 请求返回结果解析JSON失败: %s", sendResult));
        }
        if (resultJson.size() == 0) {
            result.setSuccess(false);
            logger.debug(String.format("余额查询 >> 供应商账号: %s -> 余额查询没有返回信息", domain.getUserId()));
            result.setModel(ResultDO.FIRST_MODEL_KEY, "查询失败");
            return result;
        }
        String code = null;
        String balance = null;
        String msg = null;
        try {
            code = resultJson.getString("code");
            balance = resultJson.getString("balance");
            msg = resultJson.getString("msg");
        } catch (Exception e) {
            logger.debug(String.format("余额查询 >> 获取返回信息失败: 供应商账号%s -> %s", domain.getUserId(), sendResult));
        }
        if (code == null) {
            result.setSuccess(false);
            logger.debug(String.format("余额查询 >> 供应商账号 %s -> 余额查询失败,没有返回状态码", domain.getUserId()));
            result.setModel(ResultDO.FIRST_MODEL_KEY, "查询失败");
            return result;
        }
        if("0".equals(code)){
            result.setModel(ResultDO.FIRST_MODEL_KEY, balance);
        } else {
            logger.debug(String.format("余额查询 >> 供应商账号 %s -> 余额查询失败 | %s", domain.getUserId(), code));
            result.setModel(ResultDO.FIRST_MODEL_KEY, TemplateUtils.isEmpty(msg) ? "查询失败" : msg);
        }
        return result;
    }

    /**
     * <h2>提交订单返回失败</h2>
     *
     * @param success    是否处理成功
     * @param code       订单状态
     * @param logMessage 失败信息
     * @param message    供货摘要
     * @param entity     返回信息字符串
     * @return FIRST_MODEL_KEY: 订单状态
     * SECOND_MODEL_KEY: 供货摘要
     * THIRD_MODEL_KEY: 上游返回信息字符串
     */
    private ResultDO returnFail(boolean success, int code, String logMessage, String message, String entity) {
        ResultDO result = new ResultSupport();
        result.setSuccess(success);
        result.setModel(ResultDO.FIRST_MODEL_KEY, code);
        result.setModel(ResultDO.SECOND_MODEL_KEY, message);
        result.setModel(ResultDO.THIRD_MODEL_KEY, entity);
        logger.debug(logMessage);
        return result;
    }
}
